#ifndef __SLOG_H__
#define __SLOG_H__

#define SLOG_SIMPLIFICATION 0 // 使用极简版本

#if SLOG_SIMPLIFICATION

#include <assert.h>
#include <stdint.h>
#include <stdio.h>

typedef enum
{
    _LOG_LEVEL_NONE = 0, // 静音
    _LOG_LEVEL_FATAL,    // 致命
    _LOG_LEVEL_ERROR,    // 错误
    _LOG_LEVEL_WARNING,  // 警告
    _LOG_LEVEL_NOTICE,   // 通知
    _LOG_LEVEL_INFO,     // 信息
    _LOG_LEVEL_DEBUG,    // 调试
    _LOG_LEVEL_NOISY,    // 嘈杂

} slog_level; // 日志等级

#define LOG_LEVEL _LOG_LEVEL_DEBUG // 当前日志等级

#define LOG_NONE         "\033[m"
#define LOG_RED          "\033[0;32;31m"
#define LOG_LIGHT_RED    "\033[1;31m"
#define LOG_GREEN        "\033[0;32;32m"
#define LOG_LIGHT_GREEN  "\033[1;32m"
#define LOG_BLUE         "\033[0;32;34m"
#define LOG_LIGHT_BLUE   "\033[1;34m"
#define LOG_DARY_GRAY    "\033[1;30m"
#define LOG_CYAN         "\033[0;36m"
#define LOG_LIGHT_CYAN   "\033[1;36m"
#define LOG_PURPLE       "\033[0;35m"
#define LOG_LIGHT_PURPLE "\033[1;35m"
#define LOG_BROWN        "\033[0;33m"
#define LOG_YELLOW       "\033[1;33m"
#define LOG_LIGHT_GRAY   "\033[0;37m"
#define LOG_WHITE        "\033[1;37m"

#if LOG_LEVEL >= _LOG_LEVEL_NOISY
#define slog_noisy(format, ...)                                                                                                                                \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        printf(LOG_DARY_GRAY "[ ] " LOG_NONE format "\r\n", ##__VA_ARGS__);                                                                                    \
    } while (0)
#else
#define slog_noisy(format, ...) ((void)0)
#endif

#if LOG_LEVEL >= _LOG_LEVEL_DEBUG
#define slog_debug(format, ...)                                                                                                                                \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        printf(LOG_DARY_GRAY "[D] " LOG_NONE format "\r\n", ##__VA_ARGS__);                                                                                    \
    } while (0)
#else
#define slog_debug(format, ...) ((void)0)
#endif

#if LOG_LEVEL >= _LOG_LEVEL_INFO
#define slog_info(format, ...)                                                                                                                                 \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        printf(LOG_GREEN "[I] " LOG_NONE format "\r\n", ##__VA_ARGS__);                                                                                        \
    } while (0)
#else
#define slog_info(format, ...) ((void)0)
#endif

#if LOG_LEVEL >= _LOG_LEVEL_NOTICE
#define slog_notice(format, ...)                                                                                                                               \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        printf(LOG_GREEN "[N] " LOG_NONE format "\r\n", ##__VA_ARGS__);                                                                                        \
    } while (0)
#else
#define slog_info(format, ...) ((void)0)
#endif

#if LOG_LEVEL >= _LOG_LEVEL_WARNING
#define slog_warning(format, ...)                                                                                                                              \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        printf("\r\n" LOG_YELLOW "[W] " LOG_NONE format "\r\n", ##__VA_ARGS__);                                                                                \
    } while (0)
#else
#define slog_warning(format, ...) ((void)0)
#endif

#if LOG_LEVEL >= _LOG_LEVEL_ERROR
#define slog_error(format, ...)                                                                                                                                \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        printf("\r\n" LOG_LIGHT_RED "[E] " format LOG_NONE "\r\n", ##__VA_ARGS__);                                                                             \
    } while (0)
#else
#define slog_error(format, ...) ((void)0)
#endif

#if LOG_LEVEL >= _LOG_LEVEL_ERROR
#define slog_fatal(format, ...)                                                                                                                                \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        printf("\r\n" LOG_LIGHT_RED "[F] " format LOG_NONE "\r\n", ##__VA_ARGS__);                                                                             \
    } while (0)
#else
#define slog_fatal(format, ...) ((void)0)
#endif

#define slog_assert(expr) assert(expr)

#define slog_hexdump(level, data, size, format, ...) ((void)0)

// 以下内容为兼容接口而做

typedef int (*out_func_t)(const char *format, ...);
typedef uint32_t (*get_timestamp_func_t)(void);
typedef int (*timestamp2string_func_t)(int, char *);
typedef int (*mutex_lock_func_t)(void *);
typedef int (*mutex_unlock_func_t)(void *);

struct slog_operate
{
    out_func_t              out;              // 输出函数 如：stdio中的printf，或自定义输出到文件
    get_timestamp_func_t    get_timestamp;    // 时间戳获取函数
    timestamp2string_func_t timestamp2string; // 自定义时间戳转字符串函数

    mutex_lock_func_t   mutex_lock;   // 互斥锁加锁函数
    mutex_unlock_func_t mutex_unlock; // 互斥锁解锁函数
};

inline int slog_init(slog_level level, struct slog_operate ops)
{
    return 0;
}
inline void set_log_level(slog_level level)
{
    return;
}

#else

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

#include <pthread.h>

#include "circular_queue.h"

/*************** 功能开关 ****************/
#define SLOG_USE_PTHREAD_MUTEX   1  // 启用多线程
#define SLOG_USE_ASYNC           0  // 启用异步
#define SLOG_USE_LOG_COLOR       1  // 启用颜色
#define SLOG_USE_PTHREAD_COND    1  // 启用条件变量

/*************** 日志长度 ****************/
#define SLOG_MSG_MAX_LEN         80 // 日志消息的最大长度
#define SLOG_TIME_STRING_MAX_LEN 14 // 时间字符串的最大长度
#define SLOG_HEX_LINE_MAX        16 // 16进制每行最多显示的字节数

/*************** 日志缓冲 ****************/
#if SLOG_USE_ASYNC
#define SLOG_BUFF_NUM 10 // 日志缓冲区的数量
#endif

/**
 * @brief  如：stdio中的printf，或自定义输出到文件
 *
 */
typedef int (*out_func_t)(const char *format, ...);

/**
 * @brief 时间戳获取函数（ms）
 *
 */
typedef uint32_t (*get_timestamp_func_t)(void);

/**
 * @brief 时间戳转字符串函数
 *
 */
typedef int (*timestamp2string_func_t)(int, char *, uint16_t size);

/**
 * @brief 互斥锁自定义实现
 *
 */
typedef int (*mutex_lock_func_t)(void *);
typedef int (*mutex_unlock_func_t)(void *);

typedef enum
{
    _LOG_LEVEL_NONE = 0, // 静音
    _LOG_LEVEL_FATAL,    // 致命
    _LOG_LEVEL_ERROR,    // 错误
    _LOG_LEVEL_WARNING,  // 警告
    _LOG_LEVEL_NOTICE,   // 通知
    _LOG_LEVEL_INFO,     // 信息
    _LOG_LEVEL_DEBUG,    // 调试
    _LOG_LEVEL_NOISY,    // 嘈杂

} slog_level; // 日志等级

typedef struct
{
    slog_level level;                     // 日志等级
    uint32_t   current_time;              // 时间戳
    char      *file_name;                 // 文件名
    char      *func_name;                 // 函数名
    int        line;                      // 行数
    char       content[SLOG_MSG_MAX_LEN]; // 输出内容
    bool       is_hexdump;                // 输出16进制数据

} slog_record; // 日志输出内容

struct slog_operate
{
    out_func_t              out;              // 输出函数
    get_timestamp_func_t    get_timestamp;    // 时间戳获取
    timestamp2string_func_t timestamp2string; // 时间戳转字符串

#if SLOG_USE_PTHREAD_MUTEX
    mutex_lock_func_t   mutex_lock;   // 互斥锁加锁函数
    mutex_unlock_func_t mutex_unlock; // 互斥锁解锁函数
#endif
};

struct slog
{
    slog_level level;

#if SLOG_USE_PTHREAD_MUTEX
    pthread_mutex_t mutex;
#endif

    bool is_refresh;

    struct slog_operate ops;
#if SLOG_USE_ASYNC
    circular_queue_t *queue;
#endif
};

int  slog_init(slog_level level, struct slog_operate ops);
void set_log_level(slog_level level);
void slog_write(slog_level level, bool is_hexdump, char *file_name, int line, char *func_name, const char *format, ...);
void _log_hexdump(slog_level level, const uint8_t *data, size_t size, const char *file, int line, const char *func, const char *fmt, ...);
void slog_assert_out(const char *file, int line, const char *func, const char *expr);

#ifdef NDEBUG // 发布模式禁用日志
#define slog_noisy(format, ...)                      ((void)0)
#define slog_debug(format, ...)                      ((void)0)
#define slog_info(format, ...)                       ((void)0)
#define slog_notice(format, ...)                     ((void)0)
#define slog_warning(format, ...)                    ((void)0)
#define slog_error(format, ...)                      ((void)0)
#define slog_fatal(format, ...)                      ((void)0)
#define slog_assert(expr)                            ((void)0)
#define slog_hexdump(level, data, size, format, ...) ((void)0)
#else
#define slog_noisy(format, ...)                                                                                                                                \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        slog_write(_LOG_LEVEL_NOISY, false, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__);                                                              \
    } while (0)

#define slog_debug(format, ...)                                                                                                                                \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        slog_write(_LOG_LEVEL_DEBUG, false, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__);                                                              \
    } while (0)

#define slog_info(format, ...)                                                                                                                                 \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        slog_write(_LOG_LEVEL_INFO, false, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__);                                                               \
    } while (0)

#define slog_notice(format, ...)                                                                                                                               \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        slog_write(_LOG_LEVEL_NOTICE, false, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__);                                                             \
    } while (0)

#define slog_warning(format, ...)                                                                                                                              \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        slog_write(_LOG_LEVEL_WARNING, false, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__);                                                            \
    } while (0)

#define slog_error(format, ...)                                                                                                                                \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        slog_write(_LOG_LEVEL_ERROR, false, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__);                                                              \
    } while (0)

#define slog_fatal(format, ...)                                                                                                                                \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        slog_write(_LOG_LEVEL_FATAL, false, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__);                                                              \
    } while (0)

#define slog_assert(expr)                                                                                                                                      \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        if (!(expr))                                                                                                                                           \
        {                                                                                                                                                      \
            slog_assert_out(__FILE__, __LINE__, __func__, #expr);                                                                                              \
            abort();                                                                                                                                           \
        }                                                                                                                                                      \
    } while (0)

#define slog_hexdump(level, data, size, format, ...)                                                                                                           \
    do                                                                                                                                                         \
    {                                                                                                                                                          \
        _log_hexdump(level, data, size, __FILE__, __LINE__, __func__, format, ##__VA_ARGS__);                                                                  \
    } while (0)
#endif

#endif
#endif